package hz.mobileproject.pda.callroll.manger;

import android.content.Context;
import android.graphics.Bitmap;
import android.os.Handler;
import android.os.Message;
import android.text.TextUtils;
import android.util.Log;

import com.blankj.utilcode.util.SPUtils;
import com.megvii.facepp.sdk.Facepp;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import hz.mobileproject.pda.R;
import hz.mobileproject.pda.callroll.entity.FaceIdEntity;
import hz.mobileproject.pda.callroll.entity.PhotoPathIdEntity;
import hz.mobileproject.pda.utils.FileUtils;
import hz.mobileproject.pda.callroll.utils.BitmapUtil;
import hz.mobileproject.pda.callroll.utils.ConUtil;

/**
 * Created by hasee on 2017/10/25.
 * 本地离线底库加载至内存
 */
public class PhotoFaceLoaderMgr {

    public static final int MESSAGE_WHAT_LOAD_FINISH = 0;
    public static final int MESSAGE_WHAT_LOAD_ITEM = 1;
    public static final int MESSAGE_WHAT_LOAD_NO_PHOTO = 2;

    private static final String TAG = PhotoFaceLoaderMgr.class.getSimpleName();

    private static PhotoFaceLoaderMgr mInstance;

    /**
     * 犯人人脸集合
     * 用于比对视频中的人脸（和犯人信息集合需要保持一致，通过犯人ID一一对应）
     */
    private List<FaceIdEntity> mFaceList = new ArrayList<>();

    private int mintSuccessPhotoNum = 0;

    private int mintMaxPhotoNum = 0;

    private ExecutorService mExecutor;

    private Facepp mFaceApp;

    private Context mContext;

    private Handler mHandler;

    private boolean mbIsRunning;

    /**
     * 加载照片任务线程
     */
    class InitPhotoRunnable implements Runnable{

        private PhotoPathIdEntity mPhotoFaceEntity;
        private String mBytePathName;

        public InitPhotoRunnable(PhotoPathIdEntity photoFaceEntity, String pathName){
            mPhotoFaceEntity = photoFaceEntity;
            mBytePathName = pathName;
        }

        @Override
        public void run() {
            //本地有文件直接读取
            if(!TextUtils.isEmpty(mBytePathName)){
                byte[] featureBytes = readByteByLocal(mBytePathName);
                mFaceList.add(new FaceIdEntity(mPhotoFaceEntity.getId(), featureBytes, 0));
                mintSuccessPhotoNum++;
                sendMesToMain(MESSAGE_WHAT_LOAD_ITEM);
                return;
            }

            //本地没有文件，重写写入
            Bitmap bitmap = BitmapUtil.getBitmapByFaceApp(mPhotoFaceEntity.getPhotoPath());
            if (bitmap == null){
                return;
            }
            byte[] imgBytes = BitmapUtil.getBGRByteByBitmap(bitmap);
            synchronized (TAG){
                Facepp.Face[] lastFaces = mFaceApp.detect(imgBytes,bitmap.getWidth(),bitmap.getHeight()
                        ,Facepp.IMAGEMODE_BGR);
                if(lastFaces.length>0){
                    for(int j=0;j<lastFaces.length;j++){
                        mFaceApp.getLandmark(lastFaces[j], Facepp.FPP_GET_LANDMARK81);
                        mFaceApp.getExtractFeature(lastFaces[j]);
                        //存入本地feature数组
                        byte[] featureArray = lastFaces[j].feature;
                        writeByteToLocal(featureArray, mPhotoFaceEntity.getId());
                        mFaceList.add(new FaceIdEntity(mPhotoFaceEntity.getId(), featureArray, 0));
                    }
                    mintSuccessPhotoNum++;
                    sendMesToMain(MESSAGE_WHAT_LOAD_ITEM);
                    Log.i(TAG,"haveFace ,mintSuccessPhotoNum = "+ mintSuccessPhotoNum +",thread = " + Thread.currentThread().getName());
                }else{
                    //TODO sp中需要删除条目
                    Log.e(TAG,"noFace ,mintSuccessPhotoNum = "+ mintSuccessPhotoNum +",thread = " + Thread.currentThread().getName());
                }
            }
        }
    }

    /**
     * 从本地读取byte数组
     * @param bytePathName
     * @return
     */
    private byte[] readByteByLocal(String bytePathName) {
        FileInputStream in= null;
        ByteArrayOutputStream out = null;
        byte[] bytes = null;
        try {
            in = new FileInputStream(FileUtils.getAppPath("byteArrayTemp") + bytePathName + ".temp");
            out = new ByteArrayOutputStream(1024);

            byte[] temp=new byte[1024];

            int size=0;

            while((size=in.read(temp))!=-1)
            {
                out.write(temp,0,size);
            }
            in.close();
            out.close();
            bytes = out.toByteArray();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        Log.i(TAG, "byte read");
        return bytes;
    }

    /**
     * 将byte数组写进本地
     * @param bytes
     * @param name
     */
    private void writeByteToLocal(byte[] bytes, String name) {
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(FileUtils.getAppPath("byteArrayTemp") + name + ".temp");
            fos.write(bytes);
            fos.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        Log.i(TAG, "byte write");
    }

    /**
     * 取出并更新已存本地的Byte数组目录
     * @return
     */
    private List<String> updateLocalByteNameList(List<PhotoPathIdEntity> pathList) {
        //取出已存的目录
        List<String> list = new ArrayList<>();
        String listJson = SPUtils.getInstance("localByteName").getString("listJson");
        Log.i(TAG, "listJson = " + listJson);
        try {
            JSONArray jsonArray = new JSONArray(listJson);
            for (int i=0;i<jsonArray.length();i++){
                JSONObject jsonObject = jsonArray.getJSONObject(i);
                String id = jsonObject.getString("id");
                list.add(id);
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }

        //存入最新的的目录
        StringBuffer sb = new StringBuffer();
        sb.append("[");
        for (int i=0;i<pathList.size();i++){
            sb.append("{\"id\":\"" + pathList.get(i).getId() + "\"}");
            if (i != pathList.size()-1){
                sb.append(",");
            }
        }
        sb.append("]");
        SPUtils.getInstance("localByteName").put("listJson", sb.toString());
        return list;
    }

    private PhotoFaceLoaderMgr(Context context){
        mContext = context;
        initFaceApp();
    }

    public static PhotoFaceLoaderMgr getInstance(Context context){
        if(null == mInstance){
            mInstance = new PhotoFaceLoaderMgr(context);
        }
        return mInstance;
    }

    private void initFaceApp() {
        mFaceApp = new Facepp();
        mFaceApp.init(mContext, ConUtil.getFileContent(mContext, R.raw.megviifacepp_0_5_2_model));
        Facepp.FaceppConfig faceAppConfig = mFaceApp.getFaceppConfig();
        faceAppConfig.detectionMode = Facepp.FaceppConfig.DETECTION_MODE_NORMAL;
        faceAppConfig.minFaceSize = 200;
        faceAppConfig.one_face_tracking = 0;
        mFaceApp.setFaceppConfig(faceAppConfig);
    }

    /**
     * 加载照片
     */
    public void initPhoto(Handler handler, List<PhotoPathIdEntity> pathList){
        //无法重复加载
        if(mbIsRunning){
            return;
        }
        mbIsRunning = true;

        mHandler = handler;
        mintSuccessPhotoNum = 0;
        mintMaxPhotoNum = 0;
        mFaceList.clear();

        if(pathList == null || pathList.size() <= 0){
            Log.e(TAG, "init photo list no item!");
            sendMesToMain(MESSAGE_WHAT_LOAD_NO_PHOTO);
            mHandler = null;
            mbIsRunning = false;
            return;
        }

        mExecutor = Executors.newFixedThreadPool(4);
        mintMaxPhotoNum = pathList.size();

        //获取本地已存的特征数组目录，并将更新为最新的已存特征数组目录
        List<String> localByteNameList = updateLocalByteNameList(pathList);
        //本地已存的特征数组目录与当前需要对比的底库犯人进行对比
        for (int i = 0; i < pathList.size(); i++) {
            boolean haveLocal = false;
            if (localByteNameList.size()>0){
                for (int j=localByteNameList.size()-1; j>=0; j--){
                    if (localByteNameList.get(j).equals(pathList.get(i).getId())){
                        //本地找到特征数组传入本地特征数组的路径，用于直接读取
                        mExecutor.execute(new Thread(new InitPhotoRunnable(pathList.get(i), localByteNameList.get(j))));
                        localByteNameList.remove(localByteNameList.get(j));
                        haveLocal = true;
                        break;
                    }
                }
            }
            if (!haveLocal){
                //本地没有找到特征数组，则重新生成并存特征数组至本地
                mExecutor.execute(new Thread(new InitPhotoRunnable(pathList.get(i), null)));
            }
        }

        //删除以前无用的本地特征数组
        for (int i=0;i<localByteNameList.size();i++){
            //FileUtils.getAppPath("byteArrayTemp") + localByteNameList.get(i) + ".temp"
            BitmapUtil.deleteImageFile(FileUtils.getAppPath("byteArrayTemp") + localByteNameList.get(i) + ".temp");
        }

        //开启线程监听线程池任务全部执行完
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    mExecutor.shutdown();
                    boolean loop = true;
                    do {
                        //阻塞，直到线程池里所有任务结束
                        loop = !mExecutor.awaitTermination(2, TimeUnit.SECONDS);
                    } while(loop);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Log.i(TAG,"finish mintSuccessPhotoNum = " + mintSuccessPhotoNum);
                sendMesToMain(MESSAGE_WHAT_LOAD_FINISH);
                mHandler = null;
                mbIsRunning = false;
                mExecutor = null;
            }
        }).start();
    }

    /**
     * 给主线程回调
     */
    private void sendMesToMain(int what){
        if(mHandler != null){
            Message mes = new Message();
            mes.what = what;
            mes.arg1 = mintSuccessPhotoNum;
            mes.arg2 = mintMaxPhotoNum;
            mHandler.sendMessage(mes);
        }
    }

    /**
     * 返回加载状态
     *  建议加载时使用户无法退出加载页面
     * @return
     */
    public boolean isRunning(){
        return mbIsRunning;
    }

    /**
     * 返回加载的所有face数据
     * @return
     */
    public synchronized List<FaceIdEntity> getFaceList() {
        return mFaceList;
    }

    /**
     * 修改对应Id的face数据的点名状态
     * 0 未点 1比对成功，上传中 2上传成功 3上传失败
     */
    public synchronized void updataFaceListItemStatusById(String id, int status){
        for (int i=0;i<mFaceList.size();i++){
            if(mFaceList.get(i).getId().equals(id)){
                mFaceList.get(i).setCallRollStatus(status);
                break;
            }
        }
    }

    /**
     * 将对应Id的face数据删除
     * @param id
     */
    public synchronized void removeFaceListItemById(String id){
        for(FaceIdEntity entity : mFaceList){
            if(entity.getId().equals(id)){
                mFaceList.remove(entity);
                break;
            }
        }
    }

}
